package pap.bodies;

import java.util.ArrayList;
import java.util.List;

import pap.Config;
import pap.Point;
import pap.Vector;
import pap.utils.UncompatibleDimensionsException;


public abstract class Body {

	private Config config;
	protected double mass = 0;
	protected Point massCenter;
	protected Vector speed;
	private List<Vector> forceList;

	public Body() {
		config = Config.getInstance();
		forceList = new ArrayList<Vector>();
	}

	/**
	 * Adds a new attraction force to the body
	 * 
	 * @param force
	 */
	public void addForce(Vector force) {
		forceList.add(force);
	}

	/**
	 * Updates the body position, after having applied all the attraction forces
	 * and store the new position in the buffer
	 * 
	 * @throws UncompatibleDimensionsException
	 *             All forces must have the same dimension
	 */
	public void updateAndStorePosition() throws UncompatibleDimensionsException {
		Vector resultant, acceleration, deltaV, deltaP;
		resultant = calculateAndGetResultant();
		acceleration = resultant.divide(mass);
		deltaV = acceleration.multiply(config.getDeltaT());
		deltaP = (speed.sum(deltaV.divide(2)))
				.multiply(config.getDeltaT() / 1000);

		updatePositionAndSpeed(deltaP, deltaV);

	}

	/**
	 * Updates the speed and the mass center of the body.
	 * 
	 * @param deltaP
	 * @param deltaV
	 * @throws UncompatibleDimensionsException
	 */
	protected void updatePositionAndSpeed(Vector deltaP, Vector deltaV)
			throws UncompatibleDimensionsException {
		massCenter = deltaP.applyAt(massCenter);
		speed = speed.sum(deltaV);
	}

	/**
	 * Calculates and returns the resultant of all the forces applied to the body
	 * 
	 * @return resultant
	 * @throws UncompatibleDimensionsException
	 */
	private Vector calculateAndGetResultant()
			throws UncompatibleDimensionsException {
		if (forceList.isEmpty())
			return new Vector(new Point(new double[] { 0, 0 }));

		Vector resultant = forceList.remove(0);

		while (forceList.size() > 0)
			resultant = resultant.sum(forceList.remove(0));

		return resultant;
	}

	public Point getMassCenter() {
		return massCenter;
	}

	public double getMass() {
		return mass;
	}

	public Vector getSpeed() {
		return speed;
	}

	public boolean intersect(Body b) {
		if (b instanceof SimpleBody)
			return intersect((SimpleBody) b);
		else
			return intersect((CompoundBody) b);

	}

	public abstract boolean intersect(SimpleBody body);

	/**
	 * Every body is intersected to a CompoundBody if is intersected of at least
	 * one of his components
	 * 
	 * @param body
	 * @return
	 */
	public boolean intersect(CompoundBody body) {

		for (SimpleBody simpleBody : body.getBodies()) {
			if (intersect(simpleBody))
				return true;
		}

		return false;
	}

}